Using XML-Based and Annotation-Based Configuration in Spring Framework
Course: Advanced Java Training
Topic: Using XML-Based and Annotation-Based Configuration
Course Link: Advanced Java Training at JustAcademy
Demo Link: Register for a Course Demo
Website: JustAcademy Official Website
1. Introduction to Spring Configuration
Spring configuration defines how an application creates, configures, and manages objects known as beans. The Spring IoC (Inversion of Control) container reads configuration metadata, creates the required objects, injects their dependencies, and manages their lifecycle.
Spring supports several ways to configure beans, including XML-based configuration, annotation-based configuration, and Java-based configuration using classes annotated with @Configuration and methods annotated with @Bean. This topic focuses on XML and annotations.
XML configuration stores bean definitions in an XML file, while annotation-based configuration places metadata directly in Java classes. Both approaches can be used to define dependencies and let Spring manage application components.
Learning Objectives
- Understand the purpose of Spring configuration metadata.
- Create and configure Spring beans using XML.
- Configure components using annotations such as
@Component, @Service, and @Repository.
- Use
@Autowired and @Qualifier to inject dependencies.
- Enable component scanning and load Spring configuration.
- Compare XML-based and annotation-based approaches.
- Understand how both approaches can work together.
2. What Is XML-Based Configuration?
XML-based configuration defines Spring beans in an external XML file. The file usually contains a <beans> root element and one or more <bean> elements. Each bean definition identifies a Java class and may specify constructor arguments, property values, references to other beans, and lifecycle methods.
Because configuration is kept outside Java source code, XML can be useful when configuration must be changed independently of application classes or when working with an existing application that already uses XML.
Example XML Configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageService"
class="com.example.service.MessageService"/>
</beans>
In this example, Spring creates a bean with the ID messageService using the class com.example.service.MessageService. The bean can then be retrieved from the Spring container or injected into another managed bean.
Important XML Elements and Attributes
| Element or Attribute |
Purpose |
<beans> |
Root element containing Spring bean definitions. |
<bean> |
Declares a bean managed by Spring. |
id |
Provides a name for identifying a bean. |
class |
Specifies the fully qualified Java class used to create the bean. |
<constructor-arg> |
Supplies a value or dependency through a constructor. |
<property> |
Configures a writable bean property, commonly through a setter. |
ref |
References another bean managed by Spring. |
value |
Supplies a literal value such as a string or number. |
3. Creating Java Classes for XML Configuration
Consider a simple application in which a notification service uses a message sender. The message sender is a dependency of the notification service.
MessageSender.java
package com.example.service;
public class MessageSender {
public void send(String message) {
System.out.println("Sending message: " + message);
}
}
NotificationService.java
package com.example.service;
public class NotificationService {
private MessageSender messageSender;
public void setMessageSender(MessageSender messageSender) {
this.messageSender = messageSender;
}
public void notifyUser() {
messageSender.send("Welcome to Spring configuration!");
}
}
The NotificationService class does not create its own MessageSender object. Instead, Spring can inject the dependency. This reduces tight coupling and makes the classes easier to test and maintain.
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageSender"
class="com.example.service.MessageSender"/>
<bean id="notificationService"
class="com.example.service.NotificationService">
<property name="messageSender" ref="messageSender"/>
</bean>
</beans>
The messageSender bean is defined first, and the notificationService bean receives it through the messageSender property. Spring resolves the reference and calls the corresponding setter when creating and configuring the service.
4. Loading XML Configuration in Java
In a traditional Spring application, ClassPathXmlApplicationContext loads XML configuration from the application's classpath.
MainApplication.java
package com.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.example.service.NotificationService;
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
NotificationService service =
context.getBean("notificationService", NotificationService.class);
service.notifyUser();
((ClassPathXmlApplicationContext) context).close();
}
}
Place applicationContext.xml in the classpath, commonly under src/main/resources in a Maven or Gradle project. Ensure the appropriate Spring Context dependency is included in the project.
Expected output:
Sending message: Welcome to Spring configuration!
5. What Is Annotation-Based Configuration?
Annotation-based configuration uses annotations in Java classes to provide metadata that Spring can interpret. Rather than declaring every component in XML, developers can mark application classes with annotations and enable component scanning so Spring discovers them automatically.
This approach keeps component configuration close to the code and is common in modern Spring applications. An annotation alone does not always register a bean: component scanning or another bean-registration mechanism must be enabled for component annotations to be detected.
Common Spring Annotations
| Annotation |
Purpose |
@Component |
Marks a general-purpose class as a Spring-managed component. |
@Service |
Marks a service-layer component; it is a specialized form of @Component. |
@Repository |
Marks a persistence-layer component and supports Spring's repository exception translation when applicable. |
@Controller |
Marks an MVC controller that handles web requests. |
@Autowired |
Requests dependency injection by Spring. |
@Qualifier |
Helps select a particular bean when multiple candidates match a dependency. |
@Value |
Injects a configured value or property expression. |
@Configuration |
Declares a class that provides bean definitions. |
@ComponentScan |
Specifies packages in which Spring should discover annotated components. |
6. Configuring Beans with Component Annotations
To configure the same example using annotations, annotate the dependency class with @Component and the service class with @Service. Use constructor injection to make the dependency explicit and support immutable fields.
MessageSender.java
package com.example.service;
import org.springframework.stereotype.Component;
@Component
public class MessageSender {
public void send(String message) {
System.out.println("Sending message: " + message);
}
}
NotificationService.java
package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class NotificationService {
private final MessageSender messageSender;
public NotificationService(MessageSender messageSender) {
this.messageSender = messageSender;
}
public void notifyUser() {
messageSender.send("Welcome to annotation-based Spring!");
}
}
When a class has one constructor, Spring can generally use it for dependency injection without requiring an explicit @Autowired annotation. The constructor makes it clear that a MessageSender is required for the service to operate.
7. Enabling Component Scanning
Spring must be told where to search for annotated components. In an XML-based application, component scanning can be enabled with the <context:component-scan> element. This is a useful hybrid approach: XML enables discovery while annotations define the components.
XML with Component Scanning
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example.service"/>
</beans>
Spring scans com.example.service and its subpackages for supported component annotations. Make sure the package name matches the actual location of your classes.
Java-Based Configuration for Annotation Scanning
Although this topic focuses on XML and annotation configuration, Java-based configuration is another common way to enable scanning:
package com.example.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.example.service")
public class AppConfig {
}
Load this configuration with AnnotationConfigApplicationContext:
package com.example;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.example.config.AppConfig;
import com.example.service.NotificationService;
public class MainApplication {
public static void main(String[] args) {
try (AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class)) {
NotificationService service =
context.getBean(NotificationService.class);
service.notifyUser();
}
}
}
8. Dependency Injection Using @Autowired
The @Autowired annotation asks Spring to resolve and inject a dependency. It can be used on constructors, fields, and setter methods. Constructor injection is generally preferred for required dependencies because it makes the object's requirements explicit and helps ensure the object is initialized correctly.
Constructor Injection Example
@Service
public class ReportService {
private final MessageSender messageSender;
@Autowired
public ReportService(MessageSender messageSender) {
this.messageSender = messageSender;
}
public void generateReport() {
messageSender.send("Report generated successfully.");
}
}
In this example, Spring looks for a suitable MessageSender bean and passes it to the constructor. With a single constructor, @Autowired is normally optional.
Field Injection Example
@Service
public class ReportService {
@Autowired
private MessageSender messageSender;
public void generateReport() {
messageSender.send("Report generated successfully.");
}
}
Field injection is concise, but it hides dependencies and makes isolated unit testing more difficult. Prefer constructor injection for most application code.
9. Resolving Multiple Beans with @Qualifier
If more than one bean implements the same interface or matches a dependency type, Spring may not know which one to inject. The @Qualifier annotation identifies the intended candidate.
Example Interface
public interface MessageSender {
void send(String message);
}
Two Implementations
@Component("emailSender")
public class EmailSender implements MessageSender {
public void send(String message) {
System.out.println("Email: " + message);
}
}
@Component("smsSender")
public class SmsSender implements MessageSender {
public void send(String message) {
System.out.println("SMS: " + message);
}
}
Injecting a Specific Implementation
@Service
public class NotificationService {
private final MessageSender messageSender;
public NotificationService(
@Qualifier("emailSender") MessageSender messageSender) {
this.messageSender = messageSender;
}
public void notifyUser() {
messageSender.send("Your account has been created.");
}
}
Here, Spring injects the bean named emailSender. Bean names and qualifiers must match the registered configuration. Other options include using @Primary to indicate a preferred candidate or defining beans explicitly in a configuration class.
10. Injecting Configuration Values with @Value
The @Value annotation injects literal values or values resolved from properties. It is useful for settings such as application names, service URLs, timeouts, and message templates.
Example Using a Property
package com.example.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class ApplicationInfo {
private final String applicationName;
public ApplicationInfo(
@Value("${app.name:SpringDemo}") String applicationName) {
this.applicationName = applicationName;
}
public void display() {
System.out.println("Application: " + applicationName);
}
}
The expression ${app.name:SpringDemo} reads the app.name property when available and uses SpringDemo as the default. Property sources must be configured appropriately for the application context in use.
11. Defining Beans Explicitly with @Bean
Sometimes a class cannot be annotated directly, such as a third-party library class. In that case, a @Configuration class can define a bean using a method annotated with @Bean.
package com.example.config;
import com.example.service.MessageSender;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MessageSender messageSender() {
return new MessageSender();
}
}
The method name normally becomes the bean name unless another name is specified. Spring manages the returned object as a bean within the application context. This is Java-based configuration rather than component annotation scanning, but it complements annotation-based development.
12. XML-Based vs Annotation-Based Configuration
| Comparison |
XML-Based |
Annotation-Based |
| Configuration location |
External XML file |
Metadata on Java classes and members |
| Bean registration |
Explicit <bean> definitions or component scanning |
Component scanning or other registration mechanisms |
| Dependency injection |
Configured through XML references, constructor arguments, or properties |
Often through constructors, @Autowired, and @Qualifier |
| Visibility |
Configuration can be inspected separately from source code |
Configuration is close to the relevant classes |
| Refactoring |
Class names and property names in XML may need manual updates |
Annotations are typically easier to navigate with IDE support |
| Best fit |
Legacy applications or externally managed configuration |
New applications and component-oriented development |
| Can be combined? |
Yes |
Yes |
13. Using XML and Annotations Together
Spring does not require an application to choose only one approach. XML can define infrastructure beans or legacy components while component scanning discovers annotated services. For example, an XML file can enable component scanning and also declare a bean explicitly.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example.service"/>
<bean id="legacyMessageFormatter"
class="com.example.legacy.MessageFormatter"/>
</beans>
This hybrid arrangement can support gradual migration from XML to annotations. Avoid defining the same component twice unintentionally, as duplicate or conflicting bean definitions can lead to ambiguity or startup errors.
14. Common Configuration Errors and Troubleshooting
| Problem |
Possible Cause |
Solution |
NoSuchBeanDefinitionException |
Bean was not registered or its name/type is incorrect. |
Check the bean definition, component annotation, and scanning package. |
NoUniqueBeanDefinitionException |
Multiple beans match the same dependency. |
Use @Qualifier, @Primary, or an explicit bean reference. |
| XML parsing error |
Invalid XML syntax or incorrect namespace/schema declaration. |
Validate tags, namespaces, schema locations, and attribute values. |
| Dependency is null |
Object was created manually rather than by Spring, or injection was not configured. |
Obtain managed objects from the container and verify dependency injection. |
| Component not discovered |
Component lies outside the scanned packages or lacks a recognized annotation. |
Correct the base package and check the component stereotype. |
| Unresolved property placeholder |
Property is missing or its source is not configured. |
Check property files, placeholder configuration, and default values. |
| Duplicate bean definition |
The same component is registered through multiple configuration paths. |
Remove duplicate registration or assign clear bean names and configuration ownership. |
15. Best Practices
- Prefer constructor injection for required dependencies.
- Use descriptive bean names when multiple implementations exist.
- Keep component scanning restricted to appropriate application packages.
- Use XML when it provides a clear benefit, such as supporting legacy configuration.
- Use annotations to keep component roles and dependencies close to the code.
- Do not manually instantiate Spring-managed services when you expect dependency injection.
- Keep environment-specific values in externalized configuration instead of hardcoding them.
- Test application-context startup to catch configuration errors early.
- Avoid registering the same class through both explicit bean definitions and component scanning unless there is a deliberate reason.
- Choose a consistent configuration strategy for each application area to keep maintenance straightforward.
16. Practical Exercise
Create a small notification application using both XML-based and annotation-based configuration.
- Create a
MessageSender interface with a send(String message) method.
- Implement the interface with
EmailSender and SmsSender.
- Register the implementations using
@Component or XML bean definitions.
- Create a
NotificationService that receives a MessageSender through constructor injection.
- Use
@Qualifier to select the email implementation.
- Load the Spring application context and call the notification service.
- Repeat the exercise using XML bean definitions and compare the configuration.
- Introduce an incorrect bean name intentionally, observe the error, and correct it.
17. Interview Questions and Answers
Q1. What is XML-based configuration in Spring?
Answer: It is a method of defining Spring beans and their dependencies in an external XML file using elements such as <bean>, <property>, and <constructor-arg>.
Q2. What is annotation-based configuration?
Answer: It uses annotations such as @Component, @Service, and @Autowired to describe components and dependency relationships. Spring must discover or otherwise register the annotated classes for them to become managed beans.
Q3. What is the purpose of @ComponentScan?
Answer: It tells Spring which packages to scan for supported component annotations. In XML configuration, a similar task can be enabled with <context:component-scan>.
Q4. What is the difference between @Component and @Service?
Answer: Both can register classes as Spring components. @Service is a specialized stereotype that communicates that the class belongs to the service layer.
Q5. Why is constructor injection often preferred?
Answer: It makes required dependencies explicit, supports immutable fields, and simplifies unit testing by allowing dependencies to be passed directly to the constructor.
Q6. What does @Qualifier do?
Answer: It helps Spring select a specific bean when multiple candidates match a dependency type.
Q7. Can XML and annotation-based configuration be used together?
Answer: Yes. For example, XML can enable component scanning and define legacy or infrastructure beans while annotated classes provide application services.
Q8. What is the difference between @Component and @Bean?
Answer: @Component marks a class for component scanning. @Bean marks a method in a configuration class whose returned object should be registered as a bean.
Q9. Why might Spring fail to find a bean?
Answer: The class may not be registered, its package may not be scanned, its XML definition may be missing, or the requested bean name or type may be incorrect.
Q10. When is XML configuration still useful?
Answer: It can be useful in legacy systems, in applications that keep configuration external to Java source code, or when gradually migrating an existing project to annotation-based configuration.
18. Quick Revision Checklist
- XML configuration declares beans through an XML application context file.
- Annotations describe component roles and dependency relationships in Java code.
- Component scanning allows Spring to discover annotated classes.
@Autowired requests dependency injection; a single constructor usually needs no explicit annotation.
@Qualifier helps resolve multiple matching bean candidates.
@Value can inject configured property values.
@Bean registers an object returned by a configuration method.
- XML and annotation-based configuration can coexist in one application.
- Bean registration, package scanning, names, and dependency types should be checked when troubleshooting startup errors.
19. Conclusion
XML-based and annotation-based configuration are two important approaches to managing Spring beans and dependencies. XML keeps bean definitions in an external file, while annotations keep component metadata close to the Java classes. Both support dependency injection and inversion of control when configured correctly.
For modern applications, component annotations and constructor injection are common choices, while XML remains useful in legacy applications and hybrid configurations. Understanding both approaches helps developers maintain existing Spring projects, troubleshoot bean creation problems, and build loosely coupled Java applications.
Explore more learning opportunities at JustAcademy's Advanced Java Training course, register for a course demo, or visit the JustAcademy website.